/**
 * 
 */
package cn.bigcontainer.alt.workflow.service.impl;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.annotation.Resource;

import cn.bigcontainer.alt.meta.dao.MDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.bigcontainer.alt.common.entity.BigMap;
import cn.bigcontainer.alt.common.util.WoDateKit;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.meta.dto.MAppDto;
import cn.bigcontainer.alt.meta.dto.MCrud;
import cn.bigcontainer.alt.meta.dto.MViewDto;
import cn.bigcontainer.alt.meta.mapper.MAppMapper;
import cn.bigcontainer.alt.meta.po.MApp;
import cn.bigcontainer.alt.meta.po.MViewProperty;
import cn.bigcontainer.alt.meta.service.MAppService;
import cn.bigcontainer.alt.sys.dto.UserDto;
import cn.bigcontainer.alt.sys.util.SysUtil;
import cn.bigcontainer.alt.workflow.dto.MTaskDefinitionDto;
import cn.bigcontainer.alt.workflow.dto.MTaskInstanceDto;
import cn.bigcontainer.alt.workflow.dto.MWorkflow;
import cn.bigcontainer.alt.workflow.mapper.MRuntimeTaskCandidateMapper;
import cn.bigcontainer.alt.workflow.mapper.MRuntimeTaskMapper;
import cn.bigcontainer.alt.workflow.mapper.MTaskInstanceMapper;
import cn.bigcontainer.alt.workflow.po.MRuntimeTask;
import cn.bigcontainer.alt.workflow.po.MRuntimeTaskCandidate;
import cn.bigcontainer.alt.workflow.po.MTaskInstance;
import cn.bigcontainer.alt.workflow.service.MWorkflowService;
import lombok.extern.slf4j.Slf4j;

/**
 * @author cailei
 * @date 2017年6月14日
 */
@Service
@Transactional(rollbackFor = { Exception.class })
@Primary
@Slf4j
public class MWorkflowServiceImpl implements MWorkflowService {

	@Autowired
	private MAppService woAppService;

	@Resource
	private MAppMapper appMapper;

	/**
	 * @param id
	 * @param req
	 * @return
	 */
	private MWorkflow getWorkflow(String id, Map<String, Object> req) {
		MApp app = appMapper.selectById(id);
		MWorkflow workflow = new MWorkflow(app, new BigMap<String, Object>().bigPutAll(req));
		return workflow;
	}

	/**
	 * @param appId
	 * @param req
	 */
	@Override
	@Transactional(rollbackFor = { Exception.class })
	public MViewDto loadListData(String appId, Map<String, Object> req) {
		MWorkflow workflow = getWorkflow(appId, req);

		MViewDto list = workflow.getListView();
		// String listFilterKey = MObjectDto.PREFIX_LIST_FILTER + list.getId();
		// list.getData().put(listFilterKey, req.get(listFilterKey));

		MViewDto search = workflow.getSearchView();
		// listFilterKey = MObjectDto.PREFIX_LIST_FILTER + workflow.getId();
		// workflow.getData().put(listFilterKey, req.get(listFilterKey));
		woAppService.loadListData(list, search);
		return list;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see wos.commons.services.IWoViewService#loadCreateForm(java.lang.String,
	 * java.lang.String, java.util.Map)
	 */
	@Override
	@Transactional(rollbackFor = { Exception.class })
	public MWorkflow loadCreateForm(String appId, String keyVal, Map<String, Object> req) {
		MWorkflow workflow = getWorkflow(appId, req);
		MViewDto form = workflow.getCreateView();
		MViewDto fnew = workflow.beforeLoadCreateForm(form, keyVal);
		if (fnew != null) {
			workflow.setCreateView(fnew);
			return workflow;
		}
		woAppService.loadCreateForm(form, keyVal);
		MTaskDefinitionDto taskDefinitionDto = workflow.getTasks().get(0);
		taskDefinitionDto.setFormView(form);
		workflow.getData().put("currentTask", taskDefinitionDto);
		workflow.afterLoadCreateForm(form, keyVal);
		return workflow;
	}

	@Resource
	private MRuntimeTaskMapper runtimeTaskMapper;

	@Resource
	private MRuntimeTaskCandidateMapper candidateMapper;

	@Resource
	private MTaskInstanceMapper taskInstanceMapper;

	@Resource
	private MDao dao;

	/**
	 * 更新流程相关字段：任务ID，审批状态等字段
	 * @param keyColumn 流程表主键列名
	 * @param keyValue 流程实例主键值
	 * @param table 流程表名
	 * @param taskId 流程任务定义ID
	 */
	private void updateProcessColumns(String keyColumn, String keyValue, String table, String taskId, int operation) {
		String status = "00";
		if ("-1".equals(taskId)) {
			if (operation == MTaskInstanceDto.OPERATION_APPROVAL) {
				status = "10";
			} else if (operation == MTaskInstanceDto.OPERATION_REFUSED_BACK || operation == MTaskInstanceDto.OPERATION_REFUSED_OVER) {
				status = "20";
			}
		}
		dao.update(String.format("update %s set mrwid = '%s', mspzt = '%s' where %s = '%s'", table, taskId, status, keyColumn, keyValue));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * wos.commons.services.IWoViewService#submitCreateForm(java.lang.String,
	 * java.util.Map)
	 */
	@Override
	@Transactional(rollbackFor = { Exception.class })
	public MViewDto submitCreateForm(String appId, Map<String, Object> req) {
		MWorkflow workflow = getWorkflow(appId, req);
		MViewDto form = workflow.getCreateView();
		// 处理表单创建事件
		MViewDto fnew = workflow.beforeSubmitCreateForm(form);
		if (fnew != null) {
			return fnew;
		}
		// 提交创建表单
		woAppService.submitCreateForm(form);
		// 获取当前用户
		UserDto currentUser = SysUtil.getCurrentUserWithException(req);
		// 获取当前任务定义
		MTaskDefinitionDto taskDefinitionDto = workflow.getTasks().get(0);
		taskDefinitionDto.setFormView(form);
		// 保存日志
		saveTaskInstance(taskDefinitionDto, currentUser, form, req, 1);
		// 获取下一个任务id
		String nextTaskId = taskDefinitionDto.getTaskRouterBean().getNextTaskId(taskDefinitionDto);
		// 如果不是结束
		if (!"-1".equals(nextTaskId)) {
			// 创建下一个任务的运行时任务
			MRuntimeTask runtimeTask = new MRuntimeTask();
			runtimeTask.setCandidates(1);
			runtimeTask.setProcessDefinitionId(taskDefinitionDto.getProcessDefinitionId());
			runtimeTask.setProcessInstanceId(taskDefinitionDto.getProcessInstanceId());
			runtimeTask.setTaskDefinitionId(nextTaskId);
			runtimeTaskMapper.insert(runtimeTask);
			// 保存候选人
			taskDefinitionDto.getTaskCandidateRouterBean().getTaskCandidates(taskDefinitionDto, workflow.getTask(nextTaskId)).stream().forEach(candidateId -> {
				MRuntimeTaskCandidate candidate = new MRuntimeTaskCandidate();
				candidate.setCandidateId(candidateId);
				candidate.setRuntimeTaskId(runtimeTask.getId());
				candidateMapper.insert(candidate);
			});
		}
		// 更新流程字段
		updateProcessColumns(workflow.getObject().getKeyProperties().get(0).getName(), (String)form.getKeyValue(), workflow.getObjectId(), nextTaskId, 1);
		// 处理表单创建事件
		workflow.afterSubmitCreateForm(form);
		return form;
	}

	private final static String taskDefinitionIdKey = "taskDefinitionId";

	@Override
	@Transactional(rollbackFor = { Exception.class })
	public MWorkflow loadUpdateForm(String appId, String keyVal, Map<String, Object> req) {
		MWorkflow workflow = getWorkflow(appId, req);
		MRuntimeTask runtimeTask = runtimeTaskMapper.selectOne(new QueryWrapper<MRuntimeTask>().eq("process_definition_id", appId).eq("process_instance_id", keyVal));
		UserDto currentUser = SysUtil.getCurrentUserWithException(req);
		if (runtimeTask != null) {
			// 当前用户是否是候选人
			List<MRuntimeTaskCandidate> candidates = candidateMapper.selectList(new QueryWrapper<MRuntimeTaskCandidate>().eq("runtime_task_id", runtimeTask.getId()));
			if (candidates.stream().filter(c -> c.getCandidateId().equals(currentUser.getId().toString())).findFirst().isPresent()) {
				MTaskDefinitionDto currentTask = workflow.getTask(runtimeTask.getTaskDefinitionId());
				workflow.getData().put("currentTask", currentTask);
				MViewDto formView = currentTask.getFormView();
				MViewDto fnew = workflow.beforeLoadUpdateForm(formView, keyVal);
				if (fnew != null) {
					formView = fnew;
				} else {
					woAppService.loadUpdateForm(formView, keyVal);
				}
				MViewProperty taskId = new MViewProperty();
				taskId.setType(MViewProperty.TYPE_HIDDEN);
				taskId.setId(taskDefinitionIdKey);
				taskId.setSubmit(0);
				taskId.setValue(currentTask.getId());
				taskId.setView(formView);
				formView.getProperties().add(taskId);
				workflow.beforeLoadUpdateForm(formView, keyVal);
			}
		}
		List<MTaskInstance> taskInstances = taskInstanceMapper.selectList(new QueryWrapper<MTaskInstance>().eq("process_instance_id", keyVal).eq("process_definition_id", appId).orderByAsc("create_time"));
		List<MTaskInstanceDto> logs = WoKit.getDtos(MTaskInstanceDto.class, taskInstances);
		for (int i = 0; i < logs.size(); i ++) {
			MTaskInstanceDto log = logs.get(i);
			MTaskDefinitionDto task = workflow.getTask(log.getTaskDefinitionId());
			MViewDto taskView = (i == 0) ? workflow.getCreateView() : task.getFormView();
			MViewDto viewDto = WoKit.cloneBySetter(taskView, MViewDto.class);
			viewDto.getProperties().clear();
			viewDto.getButtons().clear();
			viewDto.setReadonly(1);
			taskView.getProperties().stream().forEach(vp -> {
				MViewProperty newVp = WoKit.cloneBySetter(vp, MViewProperty.class);
				newVp.setValue(log.getFormValues().get(vp.getId()));
				newVp.setView(viewDto);
				viewDto.getProperties().add(newVp);
			});
			// 设置视图属性的值
			viewDto.getProperties().stream().forEach(vp -> vp.setValue(log.getFormValues().get(vp.getId())));
			log.getData().put("form", viewDto);
		}
		workflow.getData().put("logs", logs);
		return workflow;
	}

	private void saveTaskInstance (MTaskDefinitionDto taskDefinitionDto, UserDto currentUser, MViewDto form, Map<String, Object> req, int operation) {
		// 保存流程日志
		MTaskInstance taskInstance = new MTaskInstance();
		taskInstance.setAssigneeId(currentUser.getId().toString());
		// 设置表单属性值
		for (MViewProperty p : form.getProperties()) {
			taskInstance.getFormValues().put(p.getId(), req.get(p.getId()));
		}
		taskInstance.getFormValues().put(taskDefinitionDto + "_user", currentUser.getId());
		taskInstance.setOperation(operation);
		taskInstance.setName(String.format("%s于%s%s了[%s]", currentUser.getName(), WoDateKit.getNow(), MTaskInstanceDto.getOperationName(taskInstance.getOperation()),form.getName()));
		taskInstance.setProcessDefinitionId(taskDefinitionDto.getProcessDefinitionId());
		taskInstance.setTaskDefinitionId(taskDefinitionDto.getId());
		taskInstance.setProcessInstanceId(form.getKeyValue().toString());
		taskInstanceMapper.insert(taskInstance);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * wos.commons.services.IWoViewService#submitUpdateForm(java.lang.String,
	 * java.util.Map)
	 */
	@Override
	@Transactional(rollbackFor = { Exception.class })
	public MViewDto submitUpdateForm(String appId, Map<String, Object> req) {
		MWorkflow workflow = getWorkflow(appId, req);
		String taskDefinitionId = req.get(taskDefinitionIdKey).toString();
		MTaskDefinitionDto taskDefinitionDto = workflow.getTask(taskDefinitionId);
		MViewDto form = taskDefinitionDto.getFormView();
		MViewDto fnew = workflow.beforeSubmitUpdateForm(form);
		if (fnew != null) {
			return fnew;
		}
		woAppService.submitUpdateForm(form);
		// 获取当前用户
		UserDto currentUser = SysUtil.getCurrentUserWithException(req);
		// 获取当前任务定义并删除
		MRuntimeTask currentTask = runtimeTaskMapper.selectOne(new QueryWrapper<MRuntimeTask>()
				.eq("process_definition_id", workflow.getId())
				.eq("process_instance_id", form.getKeyValue().toString())
				.eq("task_definition_id", taskDefinitionId));
		runtimeTaskMapper.deleteById(currentTask.getId());
		candidateMapper.delete(new QueryWrapper<MRuntimeTaskCandidate>().eq("runtime_task_id", currentTask.getId()));
		// 保存日志
		int opertion = 1;
		if (taskDefinitionDto.getReview() == 1) {
			Optional<MViewProperty> result = form.getProperties().stream().filter(vp -> vp.getName().equals(form.getName() + "结果")).findFirst();
			if (result.isPresent()) {
				opertion = WoKit.getInt(req.get(result.get().getId()), opertion);
			}
		}
		saveTaskInstance(taskDefinitionDto, currentUser, form, req, opertion);
		// 获取下一个任务id
		String nextTaskId = taskDefinitionDto.getTaskRouterBean().getNextTaskId(taskDefinitionDto);
		// 如果不是结束
		if (!"-1".equals(nextTaskId)) {
			// 创建下一个任务的运行时任务
			MRuntimeTask runtimeTask = new MRuntimeTask();
			runtimeTask.setCandidates(1);
			runtimeTask.setProcessDefinitionId(taskDefinitionDto.getProcessDefinitionId());
			runtimeTask.setProcessInstanceId(taskDefinitionDto.getProcessInstanceId());
			runtimeTask.setTaskDefinitionId(nextTaskId);
			runtimeTaskMapper.insert(runtimeTask);
			// 保存候选人
			taskDefinitionDto.getTaskCandidateRouterBean().getTaskCandidates(taskDefinitionDto, workflow.getTask(nextTaskId)).stream().forEach(candidateId -> {
				MRuntimeTaskCandidate candidate = new MRuntimeTaskCandidate();
				candidate.setCandidateId(candidateId);
				candidate.setRuntimeTaskId(runtimeTask.getId());
				candidateMapper.insert(candidate);
			});
		}
		// 更新流程字段
		updateProcessColumns(workflow.getObject().getKeyProperties().get(0).getName(), (String)form.getKeyValue(), workflow.getObjectId(), nextTaskId, opertion);
		// 处理表单事件
		workflow.afterSubmitUpdateForm(form);
		return form;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see wos.commons.services.IWoCrudService#loadWorkflow(java.lang.String,
	 * java.util.Map)
	 */
	@Override
	public MWorkflow loadWorkflow(String appId, Map<String, Object> req) {
		return getWorkflow(appId, req);
	}

	@Override
	public MCrud loadCrudMeta(String appId, Map<String, Object> req) {
		MCrud crud = getWorkflow(appId, req);
		return crud;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see wos.engine.services.IWoCrudService#deleteInstances(java.lang.String,
	 * java.lang.String, java.util.Map)
	 */
	@Override
	public void deleteObjects(String appId, String keyVals, Map<String, Object> req) {
		MCrud crud = getWorkflow(appId, req);
		woAppService.deleteObjects(crud.getObject(), keyVals);
	}

	@Override
	public MViewProperty loadSelectorNameValues(String id, String propertyId, String propertyValue, Map<String, Object> req) {
		MApp app = appMapper.selectById(id);
		MAppDto crud = new MAppDto(app);
		MViewProperty viewProperty = crud.getViewProperty(propertyId);
		Object[] nameValues = viewProperty.getRelatedCrud().getObject().getNameValue(propertyValue);
		viewProperty.getData().put(MViewProperty.PARAM_NAME_VALUE, nameValues);
		return viewProperty;
	}
}
